home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#50 (Nov 89)
/
Inside Code
/
Meter.c
next >
Wrap
Text File
|
1989-03-16
|
43KB
|
1,510 lines
/* Meter.c - display, drive and control a meter with set point.
Useage: add 'Meter.c' to your project, and #include "Meter.h" in any
file that refers to the functions below. Add 'Math' and
#include <math.h> if you don't hand-load the sineTable[].
- the main purpose here is to show how the Control Manager works,
so we'll use functional equivalents of most of the Control Manager
routines listed in Inside Macintosh vol 1 pages 309 - 338 rather than
creating a new kind of control. In general, "Control" in IM is replaced
by "Meter" here; for example, if you're interested in
how NewControl() works see NewMeter() below. See MeterProc() in MeterMain.c
for an action procedure that is called by TrackMeter() to drive the
set point indicator up and down. Fancy stuff requiring external user-defined
resources is not included (no GetNewMeter()), nor is a default action Proc
supported (always call TrackMeter() with your action Proc or 0L). Two other
differences that no one should mind - all functions are 'C' rather than 'pascal',
and all value parameters are long.
- meterValue refers to the set point value, which is normally under user control,
and can be set by SetMtrValue().
- needleValue refers to current input to the meter, displayed by the needle
and NOT normally under user's control, set by SetNeedleValue().
- the digital display normally shows needleValue unless mouse is down in
meter background (simple equivalent of control "thumb").
- for a more detailed look at controlling what happens when the mouse is
in an indicator see ThumbControl() in MeterMain.c.
- the arrow and paging parts appear as small and large triangles (defined as
polygons), with the standard part codes.
- meter handles are linked to the owning windows' refCon field. If you want to
use this field for something else, change the refCon references below in NewMeter(),
DisposeMeter(), KillMeters(), DrawMeters(), and FindMeter().
*/
/* Do not include Meter.h, but DO #include <MacHeaders> either
explicitly here or by setting the "<MacHeaders>" option */
/* math.h is needed for the sineTable[] array, otherwise hand-load it. */
#define _ERRORCHECK_
int errno;
#include <math.h>
/* special partCode for meter, everything but the four up and down arrows.
A meter has no true thumb - the set point indicator is a skinny rotating
target, and too dangerous to grab if the meter is being used for closed-
loop control. "Track and hilite the background" just means show the set
point value in the display rather than the current needle value while the
mouse is down.*/
#define inBackground 24
#define NULL 0L
#define HASMETER 'qwer' /* arbitrary long ID, used by ValidMeter() */
/* Some #defines to vary the appearance of the meter. Since meters are
complicated beasts to draw, be prepared to fine-tune the drawing code
as well if you explore different looks! */
#define MINWIDTH 48 /* min. usable meter width */
#define TVHRATIO 1400L /* black rounded rect height/width, times 1000 */
#define TITLEHEIGHT 24 /* height of title box below meter proper, plus 4 */
/* given width of meter = w, total height is (w*TVHRATIO)/1000 + TITLEHEIGHT */
#define TEXTVOFFSET 4 /* to centre 12 pt text in the display box */
#define INNERPCT 94L /* used for defining needle and set point
lengths as a % of dial radius */
#define HALFSETWIDTH 1 /* half width of set point indicator */
#define CENTRERADIUS 3 /* radius of meter's little centre circle */
#define PGPOLYHEIGHT 3000L /* times 1000, keep it between 1000 and 3000 */
#define INCPOLYHEIGHT 2000L
/* --POLYHEIGHT is ratio of height to width of set point control
triangles times 1000 - try 1000,1414,1732,2000,3000. */
#define NUMTICKS 11 /* number of major position ticks around circumference */
#define NUMMINORTICKS 51 /* NUMMINORTICKS includes the NUMTICKS. */
#define HALFNEEDLERATIO 8 /* dial radius/HALFNEEDLERATIO = halfNeedleWidth */
#define MINDIGITS 3 /* minimum number of digits to display */
#define MAXDIGITS 5 /* maximum number of digits to display */
/* The digital display will show at least MINDIGITS for small meters, and at most
MAXDIGITS for large meters. See ShowValue(), AdjustDisplay(), and SetDisplayDivisor()
for details. As a nicety, you should update the meter title to reflect the actual
units displayed (eg if input is Volts and displayDivisor is 10, the meter name should
be something like "Volts / 10" */
static int sineTable[91]; /* set up by InitMeters() */
struct MeterRecord
{
struct MeterRecord **nextMeter; /* or NULL */
WindowPtr meterOwner;
Rect meterRect; /* includes title */
Byte meterVis;
Byte meterHilite;
long meterValue, meterMin, meterMax, needleValue;
/*ProcPtr meterAction; - not implemented */
long meterRfCon;
char title[32]; /* pascal format, length byte first */
/* some additional fields not found in a control record */
Rect incDownRect, incUpRect, pageDownRect, pageUpRect;
Rect displayRect; /* digital display */
long displayDivisor; /* power of ten */
int needleShort, needleLong, halfNeedleWidth;
int setShort, setLong, halfSetWidth;
PicHandle backgroundH;
PolyHandle incUpH, incDownH, pageUpH, pageDownH; /* triangles */
Point dialCentre;
long hasMeterID; /* = HASMETER for a meter */
};
typedef struct MeterRecord MeterRecord;
typedef MeterRecord *MeterPtr;
typedef MeterRecord **MeterHandle;
typedef void (*voidPtr)(); /* true C equiv. of procPtr */
/* Functions defined in this file */
/* external interface, mostly like Control Manager */
/* Control Manager equivalents */
MeterHandle NewMeter(WindowPtr, Rect*, char*, Boolean, long, long, long, long);
void DisposeMeter(MeterHandle);
void KillMeters(WindowPtr);
void SetMTitle(MeterHandle, char*);
void GetMTitle(MeterHandle, char*);
void HideMeter(MeterHandle);
void ShowMeter(MeterHandle);
void DrawMeters(WindowPtr);
void HiliteMeter(MeterHandle, int);
int FindMeter(Point, WindowPtr, MeterHandle*);
int TrackMeter(MeterHandle, Point, voidPtr);
int TestMeter(MeterHandle, Point);
void MoveMeter(MeterHandle, int, int);
void DragMeter(MeterHandle, Point, Rect*, Rect*, int);
void SizeMeter(MeterHandle, int); /* note proportions are fixed */
void SetMtrValue(MeterHandle, long); /* use this to move the set point */
long GetMtrValue(MeterHandle);
void SetMtrMin(MeterHandle, long);
long GetMtrMin(MeterHandle);
void SetMtrMax(MeterHandle, long);
long GetMtrMax(MeterHandle);
void SetMRefCon(MeterHandle, long);
long GetMRefCon(MeterHandle);
/* Special meter functions */
void SetNeedleValue(MeterHandle, long); /* use this to move the needle */
long GetNeedleValue(MeterHandle);
long GetDisplayDivisor(MeterHandle);
Boolean ValidMeter(MeterHandle);
int GetMaxTitlePixels(MeterHandle); /* displayable title width */
void MeterSnapshot(MeterHandle); /* draws meter to thePort
rather than meter's owning window */
/* support functions used in this file only */
void InitMeters(void);
void CreateMeterBackground(MeterHandle);
void DrawMeterBackground(MeterHandle);
void DrawMeterTitle(MeterHandle);
void DrawTriangle(MeterHandle, int);
void DrawNeedle(MeterHandle);
void DrawSet(MeterHandle);
void ShowValue(MeterHandle, Boolean);
void DrawInactiveMeter(MeterHandle);
void AdjustDisplay(MeterHandle);
void SetDisplayDivisor(MeterHandle);
void GetSinAndCos(int, long*, long*);
/* New meter handles are linked to the window's refCon field. Meter proportions are fixed;
height is determined from width, and boundRect.bottom is ignored. There is no default
action procedure. Titles are truncated on the right.*/
MeterHandle NewMeter(wPtr, boundRect, title, visible, meterValue, meterMin, meterMax, meterRfCon)
WindowPtr wPtr;
Rect *boundRect;
char *title;
Boolean visible;
long meterValue, meterMin, meterMax;
long meterRfCon;
{
int i,j;
MeterHandle mH, tempMH;
MeterPtr mPtr;
WindowPeek wPk = (WindowPeek)wPtr;
char *titlePtr;
/* Initialize sine table if necessary. */
if (sineTable[45] == 0) /* should be 707 by the way */
InitMeters();
/* Allocate and attach a new meter record. */
mH = (MeterHandle)NewHandle(sizeof(MeterRecord));
if (MemError() != noErr)
return(NULL);
mPtr = *mH;
mPtr->meterOwner = wPtr;
mPtr->nextMeter = NULL;
mPtr->hasMeterID = HASMETER;
/* refCon of WindowRecord holds first MeterHandle. */
/* New meters are inserted at beginning of list. */
tempMH = (MeterHandle)(wPk->refCon);
wPk->refCon = (long)mH;
if (tempMH && ((**tempMH).hasMeterID == HASMETER))
mPtr->nextMeter = tempMH;
/* Fill in the easy fields. */
mPtr->meterVis = visible;
mPtr->meterHilite = 0; /* active */
/* meterMin <= meterValue <= meterMax */
if (meterMax < meterMin)
{
DisposeMeter(mH);
return(NULL);
}
if (meterValue < meterMin)
meterValue = meterMin;
if (meterValue > meterMax)
meterValue = meterMax;
if (meterMin == meterMax)
(**mH).meterHilite = 255; /* inactive - see IM 1-327 */
mPtr->meterValue = meterValue;
mPtr->meterMin = meterMin;
mPtr->meterMax = meterMax;
mPtr->meterRfCon = meterRfCon;
mPtr->needleValue = meterMin; /* default value */
j = title[0];
if (j > 31)
j = 31;
titlePtr = mPtr->title;
for (i = 0; i <= j; ++i)
*titlePtr++ = *title++;
(**mH).meterRect = *boundRect;
(**mH).halfSetWidth = HALFSETWIDTH;
/* Do the grunt work. */
CreateMeterBackground(mH);
/* Draw meter if visible. */
if (visible)
{
(**mH).meterVis = FALSE; /* forces drawing */
ShowMeter(mH);
}
return(mH);
} /* end NewMeter() */
void DisposeMeter(mH)
MeterHandle mH;
{
Rect tempRect;
PicHandle tempPH;
PolyHandle tempPoly;
MeterHandle tMH;
GrafPtr savePort;
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
tempPH = (**mH).backgroundH;
KillPicture(tempPH);
tempPoly = (**mH).incUpH;
KillPoly(tempPoly);
tempPoly = (**mH).incDownH;
KillPoly(tempPoly);
tempPoly = (**mH).pageUpH;
KillPoly(tempPoly);
tempPoly = (**mH).pageDownH;
KillPoly(tempPoly);
tempRect = (**mH).meterRect;
EraseRect(&tempRect);
InvalRect(&tempRect);
/* Unlink the meter. */
tMH = (MeterHandle)(((WindowPeek)((**mH).meterOwner))->refCon);
if (tMH != mH)
{
while ((**tMH).nextMeter != mH)
tMH = (**tMH).nextMeter;
(**tMH).nextMeter = (**mH).nextMeter;
}
else
((WindowPeek)((**mH).meterOwner))->refCon = (long)((**mH).nextMeter);
DisposHandle((Handle)mH);
SetPort(savePort);
} /* end DisposeMeter() */
void KillMeters(wPtr)
WindowPtr wPtr;
{
WindowPeek wPk = (WindowPeek)wPtr;
while (wPk->refCon && ((**((MeterHandle)(wPk->refCon))).hasMeterID == HASMETER))
DisposeMeter((MeterHandle)(wPk->refCon));
} /* end KillMeters() */
void SetMTitle(mH, title)
MeterHandle mH;
char *title;
{
int j, i = title[0];
GrafPtr savePort;
Rect tempRect;
if (!ValidMeter(mH)) return;
if (i > 31)
i = 31;
if (i)
{
for (j = 0; j <= i; ++j)
(**mH).title[j] = title[j];
GetPort(&savePort);
SetPort((**mH).meterOwner);
DrawMeterTitle(mH);
SetPort(savePort);
}
} /* end SetMTitle() */
void GetMTitle(mH, title)
MeterHandle mH;
char *title;
{
int j, i = (**mH).title[0];
if (!ValidMeter(mH))
{
title[0] = 0;
return;
}
for (j = 0; j <=i; ++j)
title[j] = (**mH).title[j];
} /* end GetMTitle() */
void HideMeter(mH)
MeterHandle mH;
{
Rect tempRect;
GrafPtr savePort;
if (!ValidMeter(mH)) return;
if (!(**mH).meterVis) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
(**mH).meterVis = FALSE;
tempRect = (**mH).meterRect;
EraseRect(&tempRect);
InvalRect(&tempRect);
SetPort(savePort);
} /* end HideMeter() */
void ShowMeter(mH)
MeterHandle mH;
{
int i;
GrafPtr savePort;
if (!ValidMeter(mH)) return;
if ((**mH).meterVis) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
(**mH).meterVis = TRUE;
DrawMeterBackground(mH);
for (i = inUpButton; i <= inPageDown; ++i)
DrawTriangle(mH, i);
DrawNeedle(mH);
DrawSet(mH);
ShowValue(mH, TRUE);
SetPort(savePort);
} /* end ShowMeter() */
void DrawMeters(wPtr)
WindowPtr wPtr;
{
int i;
GrafPtr savePort;
MeterHandle mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort(wPtr);
while (mH)
{
if ((**mH).meterVis)
{
DrawMeterBackground(mH);
for (i = inUpButton; i <= inPageDown; ++i)
DrawTriangle(mH, i);
DrawNeedle(mH);
DrawSet(mH);
ShowValue(mH, TRUE);
}
mH = (**mH).nextMeter;
}
SetPort(savePort);
} /* end DrawMeters() */
void HiliteMeter(mH, hiliteState)
MeterHandle mH;
int hiliteState;
{
int i;
GrafPtr savePort;
if (!ValidMeter(mH)) return;
if ((**mH).meterHilite == hiliteState) return;
if ((**mH).meterHilite == 255 && hiliteState != 0) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
if (inUpButton <= hiliteState && hiliteState <= inPageDown)
{
(**mH).meterHilite = hiliteState;
DrawTriangle(mH, hiliteState);
}
else if (hiliteState == inBackground)
{ /* see #define at top of file */
(**mH).meterHilite = hiliteState;
ShowValue(mH, FALSE);
}
else if (hiliteState == 0) /* make normal */
{
if ((**mH).meterHilite == 255)
{
(**mH).meterHilite = 0;
DrawMeterBackground(mH);
for (i = inUpButton; i <= inPageDown; ++i)
DrawTriangle(mH, i);
DrawNeedle(mH);
DrawSet(mH);
ShowValue(mH, TRUE);
}
else if (inUpButton <= (**mH).meterHilite && (**mH).meterHilite <= inPageDown)
{
hiliteState = (**mH).meterHilite;
(**mH).meterHilite = 0;
DrawTriangle(mH, hiliteState);
}
else if ((**mH).meterHilite == inBackground)
{
(**mH).meterHilite = 0;
ShowValue(mH, TRUE);
}
}
else if (hiliteState == 255) /* make inactive */
{
(**mH).meterHilite = hiliteState;
DrawMeterBackground(mH);
}
SetPort(savePort);
} /* end HiliteMeter() */
int FindMeter(thePoint, wPtr, mHP)
Point thePoint;
WindowPtr wPtr;
MeterHandle *mHP;
{
int partCode;
Boolean foundIt = FALSE;
MeterHandle mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
while (ValidMeter(mH))
{
if (PtInRect(thePoint, &((**mH).meterRect)))
{
foundIt = TRUE;
break;
}
mH = (**mH).nextMeter;
}
if (foundIt && (partCode = TestMeter(mH, thePoint)))
{
*mHP = mH;
return(partCode);
}
*mHP = NULL;
return(0);
} /* end FindMeter() */
/* See ThumbControl() in MeterMain.c for an inside look at
tracking a moving indicator. */
int TrackMeter(mH, startPoint, actionProc)
MeterHandle mH;
Point startPoint;
voidPtr actionProc;
{
int partCode;
Point theMouse;
Rect partRect, slopRect;
Boolean pausing = FALSE;
partCode = TestMeter(mH, startPoint);
if (!partCode) return(0);
if (inUpButton <= partCode && partCode <= inPageDown)
{
/* hilite part and call actionProc while mouse down in part */
if (partCode == inUpButton)
partRect = (**mH).incUpRect;
else if (partCode == inDownButton)
partRect = (**mH).incDownRect;
else if (partCode == inPageUp)
partRect = (**mH).pageUpRect;
else if (partCode == inPageDown)
partRect = (**mH).pageDownRect;
slopRect = partRect;
InsetRect(&slopRect, -10, -10);
HiliteMeter(mH, partCode);
while (StillDown())
{
GetMouse(&theMouse);
if (PtInRect(theMouse, &slopRect))
{
if (pausing)
{
pausing = FALSE;
HiliteMeter(mH, partCode);
}
if (actionProc)
(*actionProc)(mH, partCode);
}
else
{
if (!pausing)
{
pausing = TRUE;
HiliteMeter(mH, 0);
}
}
}
if (!pausing)
HiliteMeter(mH, 0);
}
else if (partCode == inBackground)
{
ShowValue(mH, FALSE);
while (StillDown())
{
if (actionProc)
(*actionProc)();
}
ShowValue(mH, TRUE);
}
else return(0);
} /* end TrackMeter() */
int TestMeter(mH, thePoint)
MeterHandle mH;
Point thePoint;
{
int partCode;
if (!ValidMeter(mH)) return(0);
if (PtInRect(thePoint, &((**mH).meterRect)))
{
if ((**mH).meterVis && ((**mH).meterHilite != 255))
{
if (PtInRect(thePoint, &((**mH).incUpRect)))
partCode = inUpButton;
else if (PtInRect(thePoint, &((**mH).incDownRect)))
partCode = inDownButton;
else if (PtInRect(thePoint, &((**mH).pageUpRect)))
partCode = inPageUp;
else if (PtInRect(thePoint, &((**mH).pageDownRect)))
partCode = inPageDown;
else
partCode = inBackground;
return(partCode);
}
}
return(0);
} /* end TestMeter() */
void MoveMeter(mH, h, v)
MeterHandle mH;
int h,v;
{
Boolean wasVisible = (**mH).meterVis;
if (!ValidMeter(mH)) return;
if (wasVisible)
HideMeter(mH);
OffsetRect(&((**mH).meterRect), h, v);
OffsetRect(&((**mH).incDownRect), h, v);
OffsetRect(&((**mH).incUpRect), h, v);
OffsetRect(&((**mH).pageDownRect), h, v);
OffsetRect(&((**mH).pageUpRect), h, v);
OffsetRect(&((**mH).displayRect), h, v);
(**mH).dialCentre.h += h;
(**mH).dialCentre.v += v;
if (wasVisible)
ShowMeter(mH);
} /* end MoveMeter() */
void DragMeter(mH, startPoint, limitRect, slopRect, axis)
MeterHandle mH;
Point startPoint;
Rect *limitRect;
Rect *slopRect;
int axis;
{
long posOffset;
int h,v;
Rect mRect, tRect;
GrafPtr savePort;
RgnHandle tempRgn;
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
tempRgn = NewRgn();
mRect = (**mH).meterRect;
mRect.bottom -= TITLEHEIGHT;
tRect.left = (**mH).meterRect.left + 2;
tRect.right = (**mH).meterRect.right - 2;
tRect.bottom = (**mH).meterRect.bottom - 2;
tRect.top = tRect.bottom - TITLEHEIGHT + 4;
OpenRgn();
FrameRoundRect(&mRect,mRect.right/10, mRect.bottom/10);
FrameRect(&tRect);
CloseRgn(tempRgn);
posOffset = DragGrayRgn(tempRgn,startPoint,limitRect,slopRect,axis,0L);
DisposeRgn(tempRgn);
v = HiWord(posOffset);
h = LoWord(posOffset);
if (v != -32768 && h != -32768)
MoveMeter(mH, h, v);
SetPort(savePort);
} /* end DragMeter() */
/* Meter proportions are fixed - width determines height. */
void SizeMeter(mH, w)
MeterHandle mH;
int w;
{
PicHandle tempPH;
PolyHandle tempPoly;
Boolean wasVisible = (**mH).meterVis;
if (!ValidMeter(mH)) return;
if (wasVisible)
HideMeter(mH);
/* gut the meter and recreate it */
tempPH = (**mH).backgroundH;
KillPicture(tempPH);
tempPoly = (**mH).incUpH;
KillPoly(tempPoly);
tempPoly = (**mH).incDownH;
KillPoly(tempPoly);
tempPoly = (**mH).pageUpH;
KillPoly(tempPoly);
tempPoly = (**mH).pageDownH;
KillPoly(tempPoly);
(**mH).meterRect.right = (**mH).meterRect.left + w;
CreateMeterBackground(mH);
if (wasVisible)
ShowMeter(mH);
} /* end SizeMeter() */
void SetMtrValue(mH, meterValue)
MeterHandle mH;
long meterValue;
{
GrafPtr savePort;
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
DrawSet(mH); /* erases old pointer */
/* pin value to range min:max */
if (meterValue < (**mH).meterMin)
meterValue = (**mH).meterMin;
else if (meterValue > (**mH).meterMax)
meterValue = (**mH).meterMax;
(**mH).meterValue = meterValue;
DrawSet(mH);
ShowValue(mH, FALSE);
SetPort(savePort);
} /* end SetMtrValue() */
long GetMtrValue(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).meterValue);
} /* end GetMtrValue() */
void SetMtrMin(mH, minValue)
MeterHandle mH;
long minValue;
{
GrafPtr savePort;
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
DrawSet(mH); /* erases old pointer */
DrawNeedle(mH); /* ditto needle */
if (minValue >= (**mH).meterMax)
{
(**mH).meterMin = (**mH).meterMax;
HiliteMeter(mH, 255); /* inactive - see IM 1-327 */
return;
}
(**mH).meterMin = minValue;
if ((**mH).meterValue < minValue)
(**mH).meterValue = minValue;
SetDisplayDivisor(mH);
DrawSet(mH);
DrawNeedle(mH);
ShowValue(mH, FALSE);
SetPort(savePort);
} /* end SetMtrMin() */
long GetMtrMin(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).meterMin);
} /* end GetMtrMin() */
void SetMtrMax(mH, maxValue)
MeterHandle mH;
long maxValue;
{
GrafPtr savePort;
if (!ValidMeter(mH)) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
DrawSet(mH); /* erases old pointer */
DrawNeedle(mH); /* ditto needle */
if (maxValue <= (**mH).meterMin)
{
(**mH).meterMax = (**mH).meterMin;
HiliteMeter(mH, 255); /* inactive - see IM 1-327 */
return;
}
(**mH).meterMax = maxValue;
if ((**mH).meterValue > maxValue)
(**mH).meterValue = maxValue;
SetDisplayDivisor(mH);
DrawSet(mH);
DrawNeedle(mH);
ShowValue(mH, FALSE);
SetPort(savePort);
} /* end SetMtrMax() */
long GetMtrMax(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).meterMax);
} /* end GetMtrMax() */
void SetMRefCon(mH, refCon)
MeterHandle mH;
long refCon;
{
if (!ValidMeter(mH)) return;
(**mH).meterRfCon = refCon;
} /* end SetMRefCon() */
long GetMRefCon(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).meterRfCon);
} /* end GetMRefCon() */
void SetNeedleValue(mH, value)
MeterHandle mH;
long value;
{
GrafPtr savePort;
if (!ValidMeter(mH)) return;
if ((**mH).needleValue == value) return;
GetPort(&savePort);
SetPort((**mH).meterOwner);
DrawNeedle(mH); /* erases old needle */
/* pin value to range min:max */
if (value < (**mH).meterMin)
value = (**mH).meterMin;
else if (value > (**mH).meterMax)
value = (**mH).meterMax;
(**mH).needleValue = value;
DrawNeedle(mH);
ShowValue(mH, TRUE);
SetPort(savePort);
} /* end SetNeedleValue() */
long GetNeedleValue(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).needleValue);
} /* end GetNeedleValue() */
long GetDisplayDivisor(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0L);
return((**mH).displayDivisor);
} /* end GetDisplayDivisor() */
Boolean ValidMeter(mH)
MeterHandle mH;
{
if (mH && (**mH).hasMeterID == HASMETER)
return(TRUE);
return(FALSE);
} /* end ValidMeter() */
int GetMaxTitlePixels(mH)
MeterHandle mH;
{
if (!ValidMeter(mH)) return(0);
return((**mH).meterRect.right - (**mH).meterRect.left - 4);
} /* end GetMaxTitlePixels() */
/* A meter, like a scroll bar, is very attached to its owning
window. Only MeterSnapShot() will allow you to draw a meter
in another grafPort. Set the port, and set the font in the port
to something appropriate for the meter title before the call. */
void MeterSnapshot(mH)
MeterHandle mH;
{
int i;
Byte mVis, mHilite;
if (!ValidMeter(mH)) return;
mVis = (**mH).meterVis;
mHilite = (**mH).meterHilite;
(**mH).meterVis = TRUE;
(**mH).meterHilite = 0;
DrawMeterBackground(mH);
for (i = inUpButton; i <= inPageDown; ++i)
DrawTriangle(mH, i);
DrawNeedle(mH);
DrawSet(mH);
ShowValue(mH, TRUE);
(**mH).meterVis = mVis;
(**mH).meterHilite = mHilite;
} /* end MeterSnapshot() */
/* Functions used only in this file from here on */
/* Load up a table of sines to avoid going off on a tangent (sorry).
If this table were entered by hand, <math.h> would not be needed. */
void InitMeters()
{
int i;
double x;
for (i = 0; i <= 90; ++i)
{
x = ((double)i*PI)/180.0; /* convert degrees to radians */
sineTable[i] = (int)(sin(x)*1000.0);
}
} /* end InitMeters() */
/* The real fun - create the meter background picture
and set up points and rects needed for drawing it. We could
also have created the background in a drafting program and
just scale it to the meter's rectangle, but this wouldn't be
as precise or as easy to modify. */
void CreateMeterBackground(mH)
MeterHandle mH;
{
int i, j, k, h, v, posInc;
long theSin, theCos;
long radius, xo, yo, lowestTickH, lowestTickV;
Rect boundRect, theClipRect, tempRect, innerCircle;
GrafPtr savePort;
RgnHandle clpRgn, dialRgn, innerTickRgn;
PenState pState;
PicHandle tempPicH;
PolyHandle tempPoly;
Point c, l, r; /* centre, left, right of polygons */
GetPort(&savePort);
SetPort((**mH).meterOwner);
/* adjust meterRect - meter proportions are fixed, meterRect.bottom is ignored */
boundRect = (**mH).meterRect;
if (boundRect.right - boundRect.left < MINWIDTH)
boundRect.right = boundRect.left + MINWIDTH;
boundRect.bottom = boundRect.top + ((boundRect.right - boundRect.left)*TVHRATIO)/1000;
tempRect = boundRect;
boundRect.bottom += TITLEHEIGHT;
(**mH).meterRect = boundRect;
/* first adjust the clip; */
clpRgn = NewRgn();
dialRgn = NewRgn();
innerTickRgn = NewRgn();
GetClip(clpRgn);
/* then draw meter in rectangle starting at 0,0 for convenience */
OffsetRect(&boundRect,-boundRect.left, -boundRect.top);
OffsetRect(&tempRect,-tempRect.left, -tempRect.top);
theClipRect = boundRect;
theClipRect.right += 100;
theClipRect.bottom += 200;
ClipRect(&theClipRect);
GetPenState(&pState);
tempPicH = OpenPicture(&boundRect);
PenNormal();
/* draw black rounded rect */
FillRoundRect(&tempRect,tempRect.right/10, tempRect.right/10,black);
i = tempRect.bottom;
/* and frame two white circles */
tempRect.bottom = tempRect.right; /* square it off */
InsetRect(&tempRect, tempRect.right/15, tempRect.right/15);
j = tempRect.bottom;
FillOval(&tempRect, white);
InsetRect(&tempRect, 3, 3);
radius = (tempRect.right - tempRect.left)/2;
(**mH).dialCentre.h = tempRect.left + radius;
(**mH).dialCentre.v = tempRect.top + radius;
FrameOval(&tempRect);
OpenRgn();
FrameOval(&tempRect);
CloseRgn(dialRgn);
k = radius - (radius*INNERPCT + 50L)/100L;
InsetRect(&tempRect, k, k);
innerCircle = tempRect;
OpenRgn();
FrameOval(&tempRect);
CloseRgn(innerTickRgn);
DiffRgn(dialRgn, innerTickRgn, dialRgn); /* that made a ring */
DisposeRgn(innerTickRgn);
/* white out rect for control triangles */
tempRect.top = j + j/20; /* j = bottom of larger dial circle */
tempRect.left = (**mH).dialCentre.h - radius;
tempRect.bottom = i - j/20; /* i = bottom of rounded rect */
tempRect.right = (**mH).dialCentre.h + radius;
i = j = tempRect.right - tempRect.left;
i = (i/4)*4;
tempRect.left += (j -i)/2;
tempRect.right = tempRect.left + i;
FillRect(&tempRect, white);
/* put lines between the control triangles */
MoveTo(tempRect.left + i/4, tempRect.top);
Line(0, tempRect.bottom - tempRect.top);
MoveTo(tempRect.left + i/2, tempRect.top);
Line(0, tempRect.bottom - tempRect.top);
MoveTo(tempRect.left + i/4 + i/2, tempRect.top);
Line(0, tempRect.bottom - tempRect.top);
tempRect.right = tempRect.left + i/4;
(**mH).pageDownRect = tempRect;
OffsetRect(&tempRect, i/4, 0);
(**mH).incDownRect = tempRect;
OffsetRect(&tempRect, i/4, 0);
(**mH).incUpRect = tempRect;
OffsetRect(&tempRect, i/4, 0);
(**mH).pageUpRect = tempRect;
SetClip(dialRgn);
/* dialRgn is the ring for the ticks */
PenSize(2,2);
/* Draw the major ticks around circumference of dial */
h = (**mH).dialCentre.h;
v = (**mH).dialCentre.v;
if (NUMTICKS >= 3)
{
posInc = 2500/(NUMTICKS - 1);
for (j = -1250; j <= 1250; j += posInc)
{
i = j/10;
GetSinAndCos(i, &theSin, &theCos);
xo = (radius*theSin + 500L)/1000L + h;
yo = (-radius*theCos + 500L)/1000L + v;
MoveTo(h - 1, v - 1);
LineTo(xo - 1, yo - 1);
}
}
/* and now the minor ticks */
PenSize(1,1);
if (NUMMINORTICKS >= 3)
{
posInc = 2500/(NUMMINORTICKS - 1);
for (j = -1250; j <= 1250; j += posInc)
{
i = j/10;
/* determine sine and cosine to use */
GetSinAndCos(i, &theSin, &theCos);
xo = (radius*theSin + 500L)/1000L + h;
yo = (-radius*theCos + 500L)/1000L + v;
MoveTo(h - 1, v - 1);
LineTo(xo - 1, yo - 1);
}
}
ClipRect(&theClipRect);
DisposeRgn(dialRgn);
/* White out centre - needed for clipboard copying. */
FillOval(&innerCircle, white);
/* put small black circle in centre of dial */
tempRect.top = (**mH).dialCentre.v - CENTRERADIUS;
tempRect.left = (**mH).dialCentre.h - CENTRERADIUS;
tempRect.bottom = (**mH).dialCentre.v + CENTRERADIUS;
tempRect.right = (**mH).dialCentre.h + CENTRERADIUS;
FillOval(&tempRect, black);
/* set up and draw displayRect just below dial centre */
lowestTickH = ((xo - h)*INNERPCT)/100L + h;
lowestTickV = ((yo - v)*INNERPCT)/100L + v;
tempRect.right = lowestTickH -2160/((**mH).meterRect.right - (**mH).meterRect.left);
tempRect.left = tempRect.right - 2*(tempRect.right - h);
tempRect.top = (int)lowestTickV;
tempRect.bottom = tempRect.top + 16;
(**mH).displayRect = tempRect;
AdjustDisplay(mH);
tempRect = (**mH).displayRect;
OffsetRect(&tempRect, -1, 0);
FrameRect(&tempRect);
MoveTo(tempRect.left + 1, tempRect.bottom);
LineTo(tempRect.right, tempRect.bottom);
MoveTo(tempRect.right, tempRect.top + 1);
LineTo(tempRect.right, tempRect.bottom);
InsetRect(&tempRect, 1, 1);
i = (**mH).meterRect.left;
j = (**mH).meterRect.top;
OffsetRect(&tempRect, i,j);
(**mH).displayRect = tempRect;
SetDisplayDivisor(mH);
ClosePicture();
(**mH).backgroundH = tempPicH;
/* define up and down arrows as triangular polygons */
/* page up poly */
tempRect = (**mH).pageUpRect;
l.h = tempRect.left + (tempRect.right -tempRect.left)/8;
r.h = tempRect.right - (tempRect.right -tempRect.left)/8;
i = r.h - l.h; /* side length of triangle */
i = (i/2)*2;
r.h = l.h + i;
j = (i*PGPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2;
c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
l.v = c.v + j;
r.v = c.v + j;
tempPoly = OpenPoly();
MoveTo(c.h, c.v);
LineTo(l.h, l.v);
LineTo(r.h, r.v);
LineTo(c.h, c.v);
ClosePoly();
(**mH).pageUpH = tempPoly;
/* inc up poly */
tempRect = (**mH).incUpRect;
l.h = tempRect.left + (tempRect.right - tempRect.left)/4;
r.h = tempRect.right - (tempRect.right - tempRect.left)/4;
i = r.h - l.h; /* side length of triangle */
i = (i/2)*2;
r.h = l.h + i;
j = (i*INCPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2;
c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
l.v = c.v + j;
r.v = c.v + j;
tempPoly = OpenPoly();
MoveTo(c.h, c.v);
LineTo(l.h, l.v);
LineTo(r.h, r.v);
LineTo(c.h, c.v);
ClosePoly();
(**mH).incUpH = tempPoly;
/* inc down poly */
tempRect = (**mH).incDownRect;
l.h = tempRect.left + (tempRect.right - tempRect.left)/4;
r.h = tempRect.right - (tempRect.right - tempRect.left)/4;
i = r.h - l.h; /* side length of triangle */
i = (i/2)*2;
r.h = l.h + i;
j = (i*INCPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2 + j;
c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
l.v = c.v - j;
r.v = c.v - j;
tempPoly = OpenPoly();
MoveTo(c.h, c.v);
LineTo(l.h, l.v);
LineTo(r.h, r.v);
LineTo(c.h, c.v);
ClosePoly();
(**mH).incDownH = tempPoly;
/* page down poly */
tempRect = (**mH).pageDownRect;
l.h = tempRect.left + (tempRect.right -tempRect.left)/8;
r.h = tempRect.right - (tempRect.right -tempRect.left)/8;
i = r.h - l.h; /* side length of triangle */
i = (i/2)*2;
r.h = l.h + i;
j = (i*PGPOLYHEIGHT + 1000)/2000; /* vertical height, 1/2side * root3 */
c.v = tempRect.top + (tempRect.bottom - tempRect.top - j)/2 + j;
c.h = tempRect.left + (tempRect.right - tempRect.left)/2;
l.v = c.v - j;
r.v = c.v - j;
tempPoly = OpenPoly();
MoveTo(c.h, c.v);
LineTo(l.h, l.v);
LineTo(r.h, r.v);
LineTo(c.h, c.v);
ClosePoly();
(**mH).pageDownH = tempPoly;
/* set up needle and set point parameters */
radius = (radius*(INNERPCT-2L) + 50L)/100;
(**mH).needleLong = radius;
(**mH).setLong = radius;
(**mH).needleShort = (lowestTickV - (**mH).dialCentre.v)/2;
(**mH).setShort = (**mH).needleShort;
(**mH).halfNeedleWidth = radius/HALFNEEDLERATIO;
/* move rects and points to proper position */
i = (**mH).meterRect.left;
j = (**mH).meterRect.top;
OffsetRect(&((**mH).incDownRect), i,j);
OffsetRect(&((**mH).incUpRect), i,j);
OffsetRect(&((**mH).pageDownRect), i,j);
OffsetRect(&((**mH).pageUpRect), i,j);
(**mH).dialCentre.h += i;
(**mH).dialCentre.v += j;
/* clean up on the way out */
SetPenState(&pState);
SetClip(clpRgn);
DisposeRgn(clpRgn);
SetPort(savePort);
} /* end CreateMeterBackground() */
void DrawMeterBackground(mH)
MeterHandle mH;
{
PicHandle tempPH = (**mH).backgroundH;
Rect tempRect;
PenState pState;
GetPenState(&pState);
PenNormal();
tempRect = (**mH).meterRect;
EraseRect(&tempRect);
if ((**mH).meterVis)
{
if ((**mH).meterHilite != 255)
DrawPicture(tempPH, &tempRect);
else
DrawInactiveMeter(mH);
DrawMeterTitle(mH);
}
SetPenState(&pState);
} /* end DrawMeterBackground() */
/* Title is centered and truncated if necessary, in whatever
font is current. Drawing is clipped to the intersection of
the title rect and the current clip region. */
void DrawMeterTitle(mH)
MeterHandle mH;
{
int i,j, height;
Rect tempRect;
char title[32];
FontInfo tempInfo;
PenState pState;
RgnHandle oldClipRgn, titleClipRgn;
ControlHandle cHH, cHV;
char *mTPtr = (**mH).title;
GetFontInfo(&tempInfo);
height = tempInfo.ascent + tempInfo.descent + tempInfo.leading;
/* copy title - text must not be referenced by
an unlocked handle when calling DrawString() */
for (i = 0; i < 31; ++i)
title[i] = mTPtr[i];
/* draw shadowed title box, effective height =
TITLEHEIGHT - 4, just below black rounded rect */
tempRect.left = (**mH).meterRect.left + 2;
tempRect.right = (**mH).meterRect.right - 2;
tempRect.bottom = (**mH).meterRect.bottom - 2;
tempRect.top = tempRect.bottom - TITLEHEIGHT + 4;
FrameRect(&tempRect);
MoveTo(tempRect.left + 1, tempRect.bottom);
LineTo(tempRect.right, tempRect.bottom);
MoveTo(tempRect.right, tempRect.top + 1);
LineTo(tempRect.right, tempRect.bottom);
i = StringWidth(title);
j = tempRect.right - tempRect.left;
if ((j - i) < 2) /* title too long - truncate */
{
while (StringWidth(title) > j - 2)
title[0] -= 1;
i = StringWidth(title);
}
GetFontInfo(&tempInfo);
height = tempInfo.ascent + tempInfo.descent + tempInfo.leading;
MoveTo(tempRect.left + (j - i)/2,
tempRect.top + (tempRect.bottom - tempRect.top)/2 - height/2 + tempInfo.ascent);
oldClipRgn = NewRgn();
titleClipRgn = NewRgn();
GetClip(oldClipRgn);
RectRgn(titleClipRgn, &tempRect);
SectRgn(titleClipRgn, oldClipRgn, titleClipRgn);
SetClip(titleClipRgn);
GetPenState(&pState);
PenNormal();
DrawString(title);
SetPenState(&pState);
SetClip(oldClipRgn);
DisposeRgn(oldClipRgn);
DisposeRgn(titleClipRgn);
} /* end DrawMeterTitle() */
/* The four triangles (two small, two large)
correspond to the inc and page parts of a
scroll bar. */
void DrawTriangle(mH, partCode)
MeterHandle mH;
int partCode;
{
PenState pState;
PolyHandle tempPoly;
if ((**mH).meterHilite == 255) return;
if (partCode == inUpButton)
tempPoly = (**mH).incUpH;
else if (partCode == inDownButton)
tempPoly = (**mH).incDownH;
else if (partCode == inPageUp)
tempPoly = (**mH).pageUpH;
else if (partCode == inPageDown)
tempPoly = (**mH).pageDownH;
else
return;
GetPenState(&pState);
PenNormal();
OffsetPoly(tempPoly, (**mH).meterRect.left, (**mH).meterRect.top);
if (partCode == (**mH).meterHilite)
{
OffsetPoly(tempPoly, -1, -1);
FillPoly(tempPoly, black);
}
else
{/* neat way to shadow ANY polygon */
FillPoly(tempPoly, black);
FramePoly(tempPoly);
OffsetPoly(tempPoly, -1, -1);
ErasePoly(tempPoly);
FramePoly(tempPoly);
}
OffsetPoly(tempPoly, -(**mH).meterRect.left + 1, -(**mH).meterRect.top + 1);
SetPenState(&pState);
} /* end DrawTriangle() */
void DrawNeedle(mH)
MeterHandle mH;
{
long l1, l2, l3, xa, xb, xc, xd;
long theSin, theCos, ya, yb, yc, yd;
int position;
int h = (**mH).dialCentre.h;
int v = (**mH).dialCentre.v;
PenState pState;
PolyHandle needlePoly;
if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
GetPenState(&pState);
PenMode(patXor);
PenPat(black);
PenSize(1,1);
/* Determine position of needle based on value. Position
is the angle in degrees measured from 0 = straight up,
increasing clockwise, range -125:+125 */
position = (((**mH).needleValue - (**mH).meterMin)*250L)
/((**mH).meterMax - (**mH).meterMin) - 125L;
if (position < -125)
position = -125;
if (position > 125)
position = 125;
/* Determine sine and cosine to use. */
GetSinAndCos(position, &theSin, &theCos);
/* Calculate four vertices of needle indicator by
changing from polar to rectangular coord's and
then translating over to dial centre. */
l1 = (**mH).needleLong;
l2 = (**mH).needleShort;
l3 = (**mH).halfNeedleWidth;
xa = (-l2*theSin + 500L)/1000L + h;
ya = (l2*theCos + 500L)/1000L + v;
xb = (l1*theSin + 500L)/1000L + h;
yb = (-l1*theCos + 500L)/1000L + v;
xc = (-l3*theCos + 500L)/1000L + h;
yc = (-l3*theSin + 500L)/1000L + v;
xd = -xc + 2*h;
yd = -yc + 2*v;
needlePoly = OpenPoly();
MoveTo(xa, ya);
LineTo(xc, yc);
LineTo(xb, yb);
LineTo(xd, yd);
LineTo(xa, ya);
ClosePoly();
PaintPoly(needlePoly);
KillPoly(needlePoly);
SetPenState(&pState);
} /* end DrawNeedle() */
void DrawSet(mH)
MeterHandle mH;
{
long l1, l2, l3, xa, xb;
long theSin, theCos, ya, yb;
int position;
int thePenSize = (**mH).halfSetWidth*2;
int h = (**mH).dialCentre.h;
int v = (**mH).dialCentre.v;
PenState pState;
if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
GetPenState(&pState);
PenMode(patXor);
PenPat(black);
PenSize(thePenSize,thePenSize);
/* determine position of set indicator from value */
position = (((**mH).meterValue - (**mH).meterMin)*250L)
/((**mH).meterMax - (**mH).meterMin) - 125L;
if (position < -125)
position = -125;
if (position > 125)
position = 125;
/* determine sine and cosine to use */
GetSinAndCos(position, &theSin, &theCos);
/* calculate two vertices of position indicator */
l1 = (**mH).setLong;
l2 = (**mH).setShort;
l3 = (**mH).halfSetWidth;
xa = (-l2*theSin + 500L)/1000L - HALFSETWIDTH + h;
ya = (l2*theCos + 500L)/1000L - HALFSETWIDTH + v;
xb = (l1*theSin + 500L)/1000L - HALFSETWIDTH + h;
yb = (-l1*theCos + 500L)/1000L - HALFSETWIDTH + v;
MoveTo(xa, ya);
LineTo(xb, yb);
SetPenState(&pState);
} /* end DrawSet() */
void ShowValue(mH, needle)
MeterHandle mH;
Boolean needle;
{
int i;
int oldFont, oldSize;
Style oldStyle;
PenState pState;
long theValue;
Rect tempRect;
char pNumStr[16]; /* pascal */
if (!((**mH).meterVis) || (**mH).meterHilite == 255) return;
GetPenState(&pState);
PenNormal();
oldFont = thePort->txFont;
oldSize = thePort->txSize;
oldStyle = thePort->txFace;
TextFont(geneva);
TextSize(12);
TextFace(' ');
if (needle)
theValue = (**mH).needleValue;
else
theValue = (**mH).meterValue;
/* Truncate value to fit display box. */
if ((**mH).displayDivisor > 1L)
theValue /= (**mH).displayDivisor;
tempRect = (**mH).displayRect;
NumToString(theValue, pNumStr);
i = StringWidth(pNumStr);
EraseRect(&tempRect);
MoveTo(tempRect.right - i- 4,
tempRect.top + (tempRect.bottom - tempRect.top)/2 + TEXTVOFFSET);
DrawString(pNumStr);
TextFont(oldFont);
TextSize(oldSize);
TextFace(oldStyle);
SetPenState(&pState);
} /* end ShowValue() */
void DrawInactiveMeter(mH)
MeterHandle mH;
{
Rect tempRect;
tempRect = (**mH).meterRect;
tempRect.bottom -= TITLEHEIGHT;
FrameRoundRect(&tempRect,tempRect.right/10, tempRect.bottom/10);
} /* end DrawInactiveMeter() */
/* Ensure that digital display shows at least MINDIGITS and at most MAXDIGITS
plus sign - called only during creation of background meter picture. */
void AdjustDisplay(mH)
MeterHandle mH;
{
int oldFont, oldSize;
Style oldStyle;
int displayWidth = (**mH).displayRect.right - (**mH).displayRect.left;
int minusW, num4W;
int numDisplayDigits, wantedWidth, halfWidthDiff;
oldFont = thePort->txFont;
oldSize = thePort->txSize;
oldStyle = thePort->txFace;
TextFont(geneva);
TextSize(12);
TextFace(' ');
minusW = CharWidth('-');
num4W = CharWidth('4');
numDisplayDigits = (displayWidth - minusW - 8)/num4W;
if (numDisplayDigits < MINDIGITS) /* too small */
{
wantedWidth = 8 + minusW + num4W*MINDIGITS;
halfWidthDiff = (wantedWidth - displayWidth + 1)/2;
(**mH).displayRect.left -= halfWidthDiff;
(**mH).displayRect.right += halfWidthDiff;
}
else if (numDisplayDigits > MAXDIGITS) /* too big */
{
wantedWidth = 8 + minusW + num4W*MAXDIGITS;
halfWidthDiff = (displayWidth - wantedWidth -1)/2;
(**mH).displayRect.left += halfWidthDiff;
(**mH).displayRect.right -= halfWidthDiff;
}
TextFont(oldFont);
TextSize(oldSize);
TextFace(oldStyle);
} /* end AdjustDisplay() */
/* Set displayDivisor to appropriate power of ten so that as many digits
as possible are shown in the display. Called when creating meter and
also when setting meter max or min */
void SetDisplayDivisor(mH)
MeterHandle mH;
{
int oldFont, oldSize;
Style oldStyle;
int displayWidth = (**mH).displayRect.right - (**mH).displayRect.left + 2;
int minusW, num4W, numDisplayDigits, wantToDisplay, power;
long absMin, absMax;
char pNumStr[16]; /* pascal */
oldFont = thePort->txFont;
oldSize = thePort->txSize;
oldStyle = thePort->txFace;
TextFont(geneva);
TextSize(12);
TextFace(' ');
minusW = CharWidth('-');
num4W = CharWidth('4');
numDisplayDigits = (displayWidth - minusW - 8)/num4W;
absMin = ((**mH).meterMin < 0) ? - (**mH).meterMin : (**mH).meterMin;
absMax = ((**mH).meterMax < 0) ? - (**mH).meterMax : (**mH).meterMax;
if (absMin > absMax)
absMax = absMin;
NumToString(absMax, pNumStr);
wantToDisplay = (int)(pNumStr[0]);
if (wantToDisplay > numDisplayDigits) /* too many, must scale value down */
{
/* divisor = 10 raised to power (wantToDisplay - numDisplayDigits) */
power = wantToDisplay - numDisplayDigits;
(**mH).displayDivisor = 10L;
while (--power > 0)
(**mH).displayDivisor *= 10L;
}
else /* no scaling necessary */
(**mH).displayDivisor = 1L;
TextFont(oldFont);
TextSize(oldSize);
TextFace(oldStyle);
} /* end SetDisplayDivisor() */
/* Position is interpreted as an angle in degrees. The
values are retrieved from sineTable[], which is an array
of integers recording sines from 0 to 90 degrees, scaled
up by 1000. */
void GetSinAndCos(position, theSin, theCos)
int position;
long *theSin, *theCos;
{
if (position < -125 || position > 125)
{
*theSin = 0L;
*theCos = 0L;
return;
}
else if (-125 <= position && position <= -90)
{
*theSin = -sineTable[position + 180];
*theCos = -sineTable[-position - 90];
}
else if (-90 < position && position <= 0)
{
*theSin = -sineTable[-position];
*theCos = sineTable[position + 90];
}
else if (0 < position && position <= 90)
{
*theSin = sineTable[position];
*theCos = sineTable[90 - position];
}
else /* 90:125 */
{
*theSin = sineTable[180 - position];
*theCos = -sineTable[position - 90];
}
} /* end GetSinAndCos() */